
#ifndef __VEDA_JSONWRITER_H__
#define __VEDA_JSONWRITER_H__

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <sstream>
#include <iomanip>

#include "VInfra/VDebug.h"
#include "VIO/VJson.h"

namespace Veda{

static bool isControlCharacter(char ch)
{
	return ch > 0 && ch <= 0x1F;
}

static bool containsControlCharacter( const char* str )
{
	while ( *str ) 
	{
		if ( isControlCharacter( *(str++) ) )
			return true;
	}
	return false;
}
static void uintToString( uint value, char*& current )
{
	*--current = 0;
	do
	{
		*--current = (value % 10) + '0';
		value /= 10;
	}
	while ( value != 0 );
}

std::string valueToString( int value )
{
	char buffer[32];
	char* current = buffer + sizeof(buffer);
	bool isNegative = value < 0;
	if ( isNegative )
		value = -value;
	uintToString( uint(value), current );
	if ( isNegative )
		*--current = '-';
	if( current >= buffer )
		return current;
	else
		return buffer;
}

std::string valueToString( uint value )
{
	char buffer[32];
	char* current = buffer + sizeof(buffer);
	uintToString( value, current );
	if( current >= buffer )
		return current;
	else
		return buffer;
}

std::string valueToString( double value )
{
	char buffer[32];
	sprintf(buffer, "%#.16g", value); 
	char* ch = buffer + strlen(buffer) - 1;
	if (*ch != '0') 
		return buffer; // nothing to truncate, so save time
	while(ch > buffer && *ch == '0')
		--ch;
	char* last_nonzero = ch;
	while(ch >= buffer)
	{
		switch(*ch)
		{
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			--ch;
			continue;
		case '.':
			// Truncate zeroes to save bytes in output, but keep one.
			*(last_nonzero+2) = '\0';
			return buffer;
		default:
			return buffer;
		}
	}
	return buffer;
}

std::string valueToString( bool value )
{
	return value ? "true" : "false";
}
std::string valueToQuotedString( const char *value )
{
	// Not sure how to handle unicode...
	if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value ))
		return std::string("\"") + value + "\"";
	// We have to walk value and escape any special characters.
	// Appending to std::string is not efficient, but this should be rare.
	// (Note: forward slashes are *not* rare, but I am not escaping them.)
	uint maxsize = strlen(value) * 2 + 3; // allescaped+quotes+NULL
	std::string result;
	result.reserve(maxsize); // to avoid lots of mallocs
	result += "\"";
	for (const char* c=value; *c != 0; ++c)
	{
		switch(*c)
		{
		case '\"':
			result += "\\\"";
			break;
		case '\\':
			result += "\\\\";
			break;
		case '\b':
			result += "\\b";
			break;
		case '\f':
			result += "\\f";
			break;
		case '\n':
			result += "\\n";
			break;
		case '\r':
			result += "\\r";
			break;
		case '\t':
			result += "\\t";
			break;
		//case '\/':
		// Even though \/ is considered a legal escape in JSON, a bare slash is also legal, so I see no reason to escape it.
		// I hope I am not misunderstanding something.
		// blep notes: actually escaping \/ may be useful in javascript to avoid </ sequence.
		// Should add a flag to allow this compatibility mode and prevent this sequence from occurring.
		default:
			if ( isControlCharacter( *c ) )
			{
				std::ostringstream oss;
				oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*c);
				result += oss.str();
			}
			else
			{
				result += *c;
			}
			break;
		}
	}
	result += "\"";
	return result;
}

}

#endif

